package group.program.Service.impl;

import com.baidubce.qianfan.Qianfan;
import com.baidubce.qianfan.model.embedding.EmbeddingData;
import com.baidubce.qianfan.model.embedding.EmbeddingResponse;
import com.baidubce.qianfan.model.exception.ApiException;
import group.program.Config.NlpConfig;
import group.program.Service.EmbeddingService;
import group.program.mapper.FileMapper;
import group.program.mapper.LoginMapper;
import group.program.pojo.Document;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import java.math.BigDecimal;
import java.math.MathContext;
import java.nio.BufferOverflowException;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;

@Service
public class EmbeddingServiceImpl implements EmbeddingService {
    @Autowired
    private NlpConfig config;
    @Autowired
    private PdfServiceImpl pdfService;
    @Autowired
    private DocxServiceImpl docxService;
    @Autowired
    private FileMapper fileMapper;
    @Override
    public String textVectorization(MultipartFile file) {
        String fileData;
        Qianfan qianfan = new Qianfan(config.getAccessKey(), config.getSecretKey());
        if (config.getQianfan() == null) {
            config.setQianfan(qianfan);
        }
        if(file.getOriginalFilename().contains(".pdf")){
            fileData = pdfService.extractInformation(file);
        }else{
            fileData = docxService.extractInformation(file);
        }
        // 用于存储所有嵌入向量
        List<List<BigDecimal>> allEmbeddings = new ArrayList<>();
        // 将 fileData 分割成字符串列表
        List<String> splitData = splitFileData(fileData, 12);
        System.out.println(splitData);
        for (String data : splitData) {
            if (data.length() <= 16) {
                try {
                    EmbeddingResponse resp = qianfan.embedding()
                            .input(Collections.singletonList(data))
                            .execute();
                    System.out.println(data);
                    // 获取嵌入向量并添加到 allEmbeddings
                    for (EmbeddingData embeddingData : resp.getData()) {
                        allEmbeddings.add(embeddingData.getEmbedding());
                    }
                } catch (ApiException e) {
                    System.err.println("处理失败: " + data + " 错误信息: " + e.getMessage());
                }
            } else {
                System.err.println("输入超出最大长度限制: " + data);
            }
        }
        // 合并所有向量
        List<BigDecimal> mergedVector = mergeVectors(allEmbeddings);
        System.out.println("合并后的向量: " + mergedVector);
        // 将 mergedVector 转换为 byte[]
        byte[] vectorData = convertListToByteArray(mergedVector);
        fileMapper.addNewFile(file.getOriginalFilename(),fileData,vectorData);
        System.out.println("已成功上传知识文档");
        return "已成功上传知识文档："+file.getOriginalFilename();
    }

    @Override
    public Document searchFile(String question) {
        Qianfan qianfan = new Qianfan(config.getAccessKey(), config.getSecretKey());
        if (config.getQianfan() == null) {
            config.setQianfan(qianfan);
        }
        List<List<BigDecimal>> allEmbeddings = new ArrayList<>();
        List<String> splitData = splitFileData(question, 12);
        for (String data : splitData) {
            if (data.length() <= 16) {
                try {
                    EmbeddingResponse resp = qianfan.embedding()
                            .input(Collections.singletonList(data))
                            .execute();
                    System.out.println(data);
                    // 获取嵌入向量并添加到 allEmbeddings
                    for (EmbeddingData embeddingData : resp.getData()) {
                        allEmbeddings.add(embeddingData.getEmbedding());
                    }
                } catch (ApiException e) {
                    System.err.println("处理失败: " + data + " 错误信息: " + e.getMessage());
                }
            } else {
                System.err.println("输入超出最大长度限制: " + data);
            }
        }
        // 合并所有向量
        List<BigDecimal> questionVector = mergeVectors(allEmbeddings);
        System.out.println("合并后的向量: " + questionVector);
        // 将 mergedVector 转换为 byte[]
        byte[] vectorData = convertListToByteArray(questionVector);
        //查询所有文档
        List<Document> documents=fileMapper.selectAll();
        List<Document> results = new ArrayList<>();
        // 计算每个文档的余弦相似度
        for (Document document : documents) {
            double similarity = cosineSimilarity(vectorData, document.getVector());
            // 假设 Document 类有一个 setSimilarity 方法来设置相似度
            document.setSimilarity(similarity);
            results.add(document);  // 将文档添加到结果列表
        }
        // 按相似度排序
        results.sort(Comparator.comparingDouble(Document::getSimilarity).reversed());
        // 输出最相似的文档
        for (Document result : results) {
            // 假设 Document 类有一个 getId 方法
            System.out.println("Document Name: " + result.getTitle() + ", Similarity: " + result.getSimilarity());
        }
        return results.get(0);
    }

    private List<String> splitFileData(String fileData, int maxLength) {
        List<String> result = new ArrayList<>();
        int length = fileData.length();
        for (int i = 0; i < length; i += maxLength) {
            int end = Math.min(i + maxLength, length);
            result.add(fileData.substring(i, end));
        }
        return result;
    }
    private static List<BigDecimal> mergeVectors(List<List<BigDecimal>> vectors) {
        if (vectors.isEmpty()) {
            return Collections.emptyList(); // 如果没有向量，返回空列表
        }
        int dimension = vectors.get(0).size();
        List<BigDecimal> mergedVector = new ArrayList<>(Collections.nCopies(dimension, BigDecimal.ZERO));
        // 累加每个向量
        for (List<BigDecimal> vector : vectors) {
            for (int i = 0; i < dimension; i++) {
                mergedVector.set(i, mergedVector.get(i).add(vector.get(i)));
            }
        }
        // 计算平均值
        BigDecimal size = BigDecimal.valueOf(vectors.size());
        for (int i = 0; i < dimension; i++) {
            mergedVector.set(i, mergedVector.get(i).divide(size, MathContext.DECIMAL128));
        }
        return mergedVector;
    }
    public static byte[] convertListToByteArray(List<BigDecimal> vector) {
        // 计算字节数组的总大小
        // 预估总大小（每个BigDecimal的大小 + 4字节的长度信息）
        int totalBytes = vector.stream().mapToInt(bd -> bd.unscaledValue().toByteArray().length + 4).sum();
        // 创建一个字节缓冲区
        ByteBuffer byteBuffer = ByteBuffer.allocate(totalBytes);

        for (BigDecimal bd : vector) {
            byte[] bytes = bd.unscaledValue().toByteArray();
            // 检查缓冲区是否有足够的空间
            if (byteBuffer.remaining() < bytes.length + 4) { // 4字节用于存储长度
                throw new BufferOverflowException(); // 或者根据需要进行处理
            }
            byteBuffer.putInt(bytes.length); // 写入字节数组的长度
            byteBuffer.put(bytes); // 写入字节数组
        }

        return byteBuffer.array();
    }
    public static double cosineSimilarity(byte[] vector1, byte[] vector2) {
        // 找到最长向量的长度
        int maxLength = Math.max(vector1.length, vector2.length);
        // 创建新的数组并填充零
        double[] paddedVector1 = new double[maxLength];
        double[] paddedVector2 = new double[maxLength];
        // 复制原始向量的值并转换为double
        for (int i = 0; i < vector1.length; i++) {
            paddedVector1[i] = vector1[i]; // byte 转换为 double
        }
        for (int i = 0; i < vector2.length; i++) {
            paddedVector2[i] = vector2[i]; // byte 转换为 double
        }
        // 计算点积和向量的模
        double dotProduct = 0.0;
        double normA = 0.0;
        double normB = 0.0;
        for (int i = 0; i < maxLength; i++) {
            dotProduct += paddedVector1[i] * paddedVector2[i];
            normA += Math.pow(paddedVector1[i], 2);
            normB += Math.pow(paddedVector2[i], 2);
        }
        // 计算余弦相似度
        if (normA == 0.0 || normB == 0.0) {
            return 0.0; // 避免除以零的情况
        }
        return dotProduct / (Math.sqrt(normA) * Math.sqrt(normB));
    }
}
